﻿using System;
using System.Collections.Generic;
using System.Linq;
using Amib.Threading;
using MediaSage.Lib.Database;
using MediaSage.Lib.Plugin;
using MediaSage.Lib.Utilities;
using NHibernate.Criterion;

namespace MediaSage.Lib.MediaUpdate
{
    public class PluginContainer
    {
        public PluginEntity pluginEntity;
        public Guid WorkItemGuid;
    }

    /// <summary>
    /// Handles doing updates or scans for a media file
    /// This does several things:
    ///     <list type="bullet">
    ///         <item><description>Calls all of the plugins active and viable for a specific file type and metadata</description></item>
    ///         <item><description>Merges the results once all the threads complete</description></item>
    ///     </list>
    /// </summary>
    /// <remarks>Eventually this potentially should send an event that it is complete for the UI</remarks>
    public class MediaUpdate
    {
        private Configuration.Config config;
        private Logger logger;
        private ThreadManager.ThreadManager threadManager;

        /// <summary>
        /// Class intialization, sets up our configuration, and logger instances
        /// </summary>
        public MediaUpdate(Configuration.Config configuration)
        {
            this.config = configuration;
            this.logger = configuration.Logger;
            this.threadManager = configuration.ThreadManager;

            logger.Log("MediaSage.Lib.MediaUpdate.MediaUpdate() initialized");
        }

        /// <summary>
        /// This will spawn a thread to actually do the update (So its non-blocking)
        /// </summary>
        /// <param name="file">File or Directory we are updating from</param>
        public void UpdateMedia(string file)
        {
            logger.Log(String.Format("Entering UpdateMedia({0})", file));
            Guid threadID = Guid.NewGuid();
            IWorkItemResult<object> iwir =
                threadManager.GetThreadPool().QueueWorkItem(new Amib.Threading.Func<string, object>(getMediaUpdate), file);
            threadManager.GetThreadResults().Add(threadID, iwir);
            logger.Log(String.Format("Exiting UpdateMedia({0})", file));
        }

        private object getMediaUpdate(string file)
        {
            logger.Log(String.Format("Entering getMediaUpdate({0})", file));
            VideoEntity ve =
                DBUtil.GetSession().CreateCriteria(typeof(VideoEntity)).Add(Restrictions.Eq("Filename", file)).
                    UniqueResult<VideoEntity>();
            MediaClass dbMediaClass = new MediaClass(config);
            dbMediaClass.videoEntity = ve;

            var pluginOrder = new Dictionary<int, PluginContainer>();
            foreach (PluginEntity pe in config.Plugins)
            {
                if (pe.IsActive && pe.PluginType == PluginType.metadata)
                {
                    int priority = pe.Priority;
                    if (pluginOrder.ContainsKey(priority))
                    {
                        priority++;
                        while (pluginOrder.ContainsKey(priority++))
                        {
                            priority++;
                        }
                    }
                    PluginContainer pec = new PluginContainer();
                    pec.pluginEntity = pe;
                    pluginOrder.Add(priority, pec);
                }
            }

            // We now have a list of plugins and their priority (Priority will be used to merge later)

            var activeUpdates = new List<Guid>();

            foreach (KeyValuePair<int, PluginContainer> kvp in pluginOrder)
            {
                PluginBase plugin = config.PluginManager.GetPlugin(kvp.Value.pluginEntity);
                if (plugin != null)
                {
                    kvp.Value.WorkItemGuid = threadManager.CreateUpdateThread(plugin, dbMediaClass);
                    activeUpdates.Add(kvp.Value.WorkItemGuid);
                }
            }

            threadManager.WaitForResults(activeUpdates);

            // We should now have all our results, we need to go through and merge them

            var sortedList = pluginOrder.Keys.ToList();
            sortedList.Sort();

            MediaClass updatedMC;

            foreach (var key in sortedList)
            {
                Utilities.MediaUtil.MergeMediaObjects(config,
                                                      (MediaClass)threadManager.GetThreadResults()[pluginOrder[key].WorkItemGuid],
                                                      dbMediaClass, config.MergeType);
            }

            logger.Log("Saving update to media");
            dbMediaClass.StoreToDatabase();
            logger.Log(String.Format("Entering getMediaUpdate({0}), rc==null", file));
            return null;
        }
    }
}